package cn.blnp.net.flowable.event.middle.upgrade;

import cn.hutool.core.map.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <h3>中间事件-升级中间抛出事件</h3>
 *
 * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
 * @version 1.0
 * @since 2025/5/12 11:03
 */
@Slf4j
@SpringBootTest
public class MidUpgradeEvent {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;

    /**
     *  <p>
     *      升级中间抛出事件指在流程中将一个升级事件作为独立的节点来运行，是一种抛出事件。当流程流转到升级中间抛出事件时，工作流引擎会直接抛出升级事件，其他引用了与其相同升级的
     *  捕获事件都会被触发，升级抛出后升级中间抛出事件结束，流程沿其外出顺序流继续流转。升级中间抛出事件抛出的升级可以被升级边界事件捕获，或者触发一个拥有相同escalationCode或者
     *  没有escalationCode的事件子流程。具体事件定义格式：
     *  <pre>
     *      {@code
     *          <escalation id="escalation1" escalationCode="escalationCode"/>
     *          <process id="escalationIntermediateThrowingEventProcess">
     *              <intermediateThrowEvent id="intermediateThrowEvent1">
     *                  <escalationEventDefinition escalationRef="escalation1"/>
     *              </intermediateThrowEvent>
     *          </process>
     *      }
     *  </pre>
     *  </p>
     *  <h2>案例演示</h2>
     *  <hr/>
     *  <p>
     *      该流程为客服处理流程：问题上报后，流程首先经包容网关到达“一线客服处理”用户任务，如果存在质量问题，还会经包容网关到达“质量检查升级结束事件”。升级结束事件会触发“质量检查事件子流
     *   程”发起，到达“质量检查”用户任务。“质量检查”用户任务判定问题级别，如果属于一般问题，则子流程结束，如果属于严重问题，则到达“客服升级中间抛出事件”，抛出升级触发附属在该子流程边界上的
     *   “客服升级边界事件”流转到“客服经理处理”用户任务，同时子流程结束。最后，“一线客服处理”“客服经理处理”用户任务处理完成经包容网关汇聚后流程结束。<br/>
     *   <b>流程定义文件：</b>{@link flow/midUpgradeEvent.bpmn20.xml}<br/>
     *   <b>流程预览图：</b>
     *   <img src="https://cdn4.winhlb.com/2025/05/12/68216c8d00f37.png" />
     *  </p>
     **/
    @Test
    public void test() {
        Deployment deploy = repositoryService.createDeployment()
                .name("升级中间捕获事件演示")
                .addClasspathResource("flow/midUpgradeEvent.bpmn20.xml")
                .deploy();
        log.info("部署流程定义：{}", deploy.getId());
        ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .singleResult();
        log.info("流程定义编号：{}", procDef.getId());
        //启动流程实例
        ProcessInstance procInst = runtimeService.startProcessInstanceById(procDef.getId());
        TaskQuery taskQuery = taskService.createTaskQuery()
                .processInstanceId(procInst.getId());
        //查询第一个任务
        Task task1 = taskQuery.singleResult();
        log.info("流程发起后，第一个任务名称为：{}", task1.getName());
        //设置流程变量并完成第一个任务
        Map<String, Object> varMap1 = MapUtil.builder(new HashMap<String, Object>())
                //设置存在质量问题后，则抛出升级事件
                .put("isQualityIssue", true)
                        .build();
        taskService.complete(task1.getId(), varMap1);
        //查询流程当前任务
        List<Task> taskList1 = taskQuery.list();
        log.info("问题上报任务完成后，流程当前任务名称为：{}", taskList1.stream()
                .map(Task::getName).collect(Collectors.joining("，")));
        //设置流程变量，完成质量检查任务
        Map<String, Object> varMap2 = MapUtil.builder(new HashMap<String, Object>())
                //设置问题为严重问题，则流转客服经理处理
                .put("problemLeve", "serious")
                .build();
        Task task2 = taskList1.stream()
                .filter(task -> task.getName().equals("质量检查"))
                .findFirst()
                .get();
        taskService.complete(task2.getId(), varMap2);
        //查询流程当前任务
        List<Task> taskList2 = taskQuery.list();
        log.info("质量检查任务完成后，流程当前任务名称为：{}", taskList2.stream()
                .map(Task::getName).collect(Collectors.joining("，")));
        for (Task task : taskList2) {
            log.info("办理任务：{}", task.getName());
            taskService.complete(task.getId());
        }

        /**
         *  演示结果：
         *  2025-05-12 11:47:45.302  INFO 33152 --- [           main] c.b.n.f.e.m.upgrade.MidUpgradeEvent      : 部署流程定义：dd037373-2ee3-11f0-828e-50ebf6ec8f6d
         * 2025-05-12 11:47:45.304  INFO 33152 --- [           main] c.b.n.f.e.m.upgrade.MidUpgradeEvent      : 流程定义编号：midUpgradeEvent:2:dd3d9646-2ee3-11f0-828e-50ebf6ec8f6d
         * 2025-05-12 11:47:45.362  INFO 33152 --- [           main] c.b.n.f.e.m.upgrade.MidUpgradeEvent      : 流程发起后，第一个任务名称为：问题上报
         * 2025-05-12 11:47:45.430  INFO 33152 --- [           main] c.b.n.f.e.m.upgrade.MidUpgradeEvent      : 问题上报任务完成后，流程当前任务名称为：一线客服处理，质量检查
         * 2025-05-12 11:47:45.475  INFO 33152 --- [           main] c.b.n.f.e.m.upgrade.MidUpgradeEvent      : 质量检查任务完成后，流程当前任务名称为：一线客服处理，客服经理处理
         * 2025-05-12 11:47:45.475  INFO 33152 --- [           main] c.b.n.f.e.m.upgrade.MidUpgradeEvent      : 办理任务：一线客服处理
         * 2025-05-12 11:47:45.491  INFO 33152 --- [           main] c.b.n.f.e.m.upgrade.MidUpgradeEvent      : 办理任务：客服经理处理
         **/
    }
}
